<template>
	<view class="student-page">
		<view class="nav-bar">
			<!-- 搜索框 -->
			<view class="nav-search">
				<uni-search-bar v-model="searchText" radius="100" cancelButton="none"
					placeholder="点击搜索"></uni-search-bar>
			</view>
			<!-- 添加好友按钮 -->
			<view class="nav-add" @click="onAddNewStudents">
				<uni-icons type="personadd" size="35"></uni-icons>
			</view>
		</view>
		<!-- 若没有好友，显示提示信息 -->
		<view class="student-tip" v-if="students.length == 0 &&!loading">
			温馨提示：您现在还没有任何学生，快点击右上方'+'按钮添加吧~
		</view>
		<!-- 好友列表部分（有好友时显示） -->
		<view class="student-items" v-else>
			<up-index-list :index-list="studentIdxList">
				<template v-for="(students, i) in studentGroupMaps">
					<up-index-item>
						<up-index-anchor :text=studentIdxList[i] bgColor="#f2f3fd"></up-index-anchor>
						<view v-for="student in students" :key="student.id">
							<friend-item :student="student"></friend-item>
						</view>
					</up-index-item>
				</template>
			</up-index-list>
		</view>
	</view>
</template>

<script>
	import {
		reactive,
		onMounted,
		watch,
		toRefs,
		computed
	} from 'vue';

	import {
		onLoad
	} from '@dcloudio/uni-app';

	import {
		pinyin
	} from 'pinyin-pro';

	export default {
		setup() {
			const db_student = uniCloud.importObject("db_student");
			// pinyinCache用于缓存学生姓名转换后的拼音，避免重复转换
			const pinyinCache = {};
			// firstLetterCache用于缓存学生姓名以及搜索内容的拼音首字母，减少重复计算首字母的操作
			const firstLetterCache = {};
			// 创建响应式数据对象state，用于管理组件内相关的数据状态
			const state = reactive({
				// 存储学生数据的数组，初始为空
				students: [],
				// 用于双向绑定搜索框输入内容的字符串变量，初始为空
				searchText: '',
				// 用于存储学生数据按首字母分组后的映射数据
				studentGroupMaps: {},
				// 用于存储学生分组索引列表的数据，初始为空数组，后续会根据分组情况更新
				studentIdxList: [],
				// 新增loading状态，用于控制加载动画的显示与隐藏，true表示正在加载
				loading: true
			});

			// 定义添加新学生的方法，点击添加好友按钮时会调用此方法，实现页面导航到添加学生页面的功能
			const onAddNewStudents = () => {
				uni.navigateTo({
					url: "/pages/student/student-add"
				});
			};

			// 在组件挂载完成后执行的生命周期钩子函数，目前为空
			onMounted(() => {});

			// 在页面加载时执行的生命周期钩子函数
			onLoad(async () => {
				try {
					// 调用云对象的getStudentInfo方法获取学生数据，这是一个异步操作
					const studentData = await db_student.getStudentInfo();
					// 将获取到的学生数据赋值给state.students
					state.students = studentData;
					// 如果获取到的学生数据是数组且长度大于0，调用getStudentGroup方法进行分组处理
					if (Array.isArray(state.students) && state.students.length > 0) {
						getStudentGroup();
					}
				} catch (error) {
					// 如果获取学生数据出错，打印错误信息
					console.error('获取学生数据出错:', error);
				} finally {
					// 无论获取数据是否成功，最终将loading设为false，隐藏加载动画
					state.loading = false;
				}
			});

			// 监听学生数据变化
			watch(() => {
				return db_student.getStudentInfo();
			}, async (newStudents) => {
				state.students = newStudents;
				if (Array.isArray(state.students) && state.students.length > 0) {
					await getStudentGroup();
				}
			}, {
				deep: true,
				immediate: true
			});

			// 监听搜索框内容变化
			watch(() => state.searchText, async () => {
				if (Array.isArray(state.students) && state.students.length > 0) {
					await getStudentGroup();
				}
			});

			// 定义将中文转换为拼音并获取首字母的函数，用于后续对学生姓名进行首字母处理等操作
			const firstLetter = (strText) => {
				let pinyinOptions = {
					toneType: 'none',
					type: 'normal'
				};
				// 使用pinyin-pro库将输入的字符串转换为拼音
				let pyText = pinyin(strText, pinyinOptions);
				// 获取拼音的首字母并转为大写
				let firstPyLetter = pyText[0][0].toUpperCase();

				// 如果首字母是英文字母，直接返回
				if (/^[A-Za-z]+$/.test(firstPyLetter)) {
					return firstPyLetter;
				} else {
					// 定义拼音到英文首字母的映射表，处理特殊拼音情况
					let pyToEnglishMap = {
						'ZH': 'Z',
						'CH': 'C',
						'SH': 'S',
					};

					// 遍历拼音到英文首字母的映射表pyToEnglishMap
					for (let py in pyToEnglishMap) {
						// 检查转换后的拼音字符串的第一个拼音片段是否以当前映射表中的拼音开头
						if (pyText[0].startsWith(py)) {
							// 如果匹配，返回对应的英文首字母
							return pyToEnglishMap[py];
						}
					}
					// 如果遍历完映射表都没有找到匹配的拼音开头，返回空字符串
					return '';
				}
			};

			// 定义判断一个字符是否为英文字母的函数，用于后续分组等逻辑中判断首字母类型
			const isEnglish = (character) => {
				return /^[A-Za-z]+$/.test(character);
			};

			// 计算属性：按首字母分组学生数据的函数（需要配合watch手动更新相关数据）
			const getGroupMap = () => {
				const groupMap = new Map();
				// 根据searchText是否为空，决定处理全部学生还是过滤后的学生
				const targetStudents = state.searchText ?
					state.students.filter(student => {
						const studentName = student.name;
						// 尝试从缓存中获取当前学生姓名对应的拼音，如果缓存中不存在
						let studentPinyin;
						if (!pinyinCache[studentName]) {
							// 使用pinyin-pro库将学生姓名转换为拼音，并设置相关转换参数
							studentPinyin = pinyin(studentName, {
								toneType: 'none',
								type: 'normal'
							});
							// 将转换后的拼音存入缓存，方便后续使用
							pinyinCache[studentName] = studentPinyin;
						} else {
							// 如果缓存中存在，直接获取缓存中的拼音
							studentPinyin = pinyinCache[studentName];
						}
						const searchPinyin = pinyin(state.searchText, {
							toneType: 'none',
							type: 'normal'
						});
						// 尝试从缓存中获取搜索框内容的拼音首字母，如果缓存中不存在
						let searchFirstLetter;
						if (!firstLetterCache[state.searchText]) {
							// 调用firstLetter函数获取拼音首字母，并转为大写
							searchFirstLetter = firstLetter(state.searchText).toUpperCase();
							// 将获取到的拼音首字母存入缓存
							firstLetterCache[state.searchText] = searchFirstLetter;
						} else {
							// 如果缓存中存在，直接获取缓存中的拼音首字母
							searchFirstLetter = firstLetterCache[state.searchText];
						}
						// 尝试从缓存中获取当前学生姓名的拼音首字母，如果缓存中不存在
						let studentFirstLetter;
						if (!firstLetterCache[studentName]) {
							// 调用firstLetter函数获取拼音首字母，并转为大写
							studentFirstLetter = firstLetter(studentName).toUpperCase();
							// 将获取到的拼音首字母存入缓存
							firstLetterCache[studentName] = studentFirstLetter;
						} else {
							// 如果缓存中存在，直接获取缓存中的拼音首字母
							studentFirstLetter = firstLetterCache[studentName];
						}

						// 匹配逻辑：只要满足以下任意一个条件，就将该学生纳入筛选结果
						// 1. 学生姓名包含搜索框输入的内容
						// 2. 学生姓名的拼音包含搜索框内容的拼音
						// 3. 学生姓名的拼音首字母与搜索框内容的拼音首字母相同
						return studentName.includes(state.searchText) ||
							studentPinyin.includes(searchPinyin) ||
							studentFirstLetter === searchFirstLetter;
					}) :
					state.students;

				targetStudents.forEach((student) => {
					// 优先从缓存获取学生姓名对应的拼音首字母，若缓存未命中
					const letter = firstLetterCache[student.name] || firstLetter(student.name).toUpperCase();
					if (!isEnglish(letter)) {
						// 如果首字母不是英文字母，将其设为 "#"，用于后续特殊分组
						letter = "#";
					}
					if (groupMap.has(letter)) {
						// 如果Map对象groupMap已经有以该首字母为键的分组
						groupMap.get(letter).push(student);
					} else {
						// 如果没有，就以该首字母为键，创建一个新数组，放入当前学生对象
						groupMap.set(letter, [student]);
					}
				});

				const arrayObj = Array.from(groupMap);
				arrayObj.sort((a, b) => {
					if (a[0] === '#' || b[0] === '#') {
						// 如果a或b的首字母是 "#"，将 "#" 分组放到最后
						return b[0].localeCompare(a[0]);
					}
					// 正常按首字母字典序排序
					return a[0].localeCompare(b[0]);
				});
				// 再将排序后的数组转回Map对象，以便后续使用
				return new Map(arrayObj.map(i => [i[0], i[1]]));
			};

			// 定义一个名为getStudentGroup的函数，用于处理学生分组相关逻辑
			const getStudentGroup = () => {
				// 调用getGroupMap函数获取已经按首字母分组好的学生数据，返回值是一个Map类型
				const studentGroupMapData = getGroupMap();
				// 如果获取到的分组数据大小为0，意味着没有有效的分组数据
				if (studentGroupMapData.size === 0) {
					// 此时返回一个只包含'#'的数组，作为默认分组标识，后续可用于界面显示相关处理
					return ['#'];
				}
				// 如果存在有效的分组数据，将分组数据的键（即分组索引，比如首字母）提取出来，转换为数组，赋值给state.studentIdxList
				state.studentIdxList = Array.from(studentGroupMapData.keys());
				// 同时，将分组数据的值（即每个分组下对应的学生数组）提取出来，转换为数组，赋值给state.studentGroupMaps
				state.studentGroupMaps = Array.from(studentGroupMapData.values());
			};

			// 将state中的响应式数据通过toRefs进行解包，连同定义的方法等一起返回，以便在模板中使用
			return {
				...toRefs(state),
				onAddNewStudents,
				getGroupMap,
				getStudentGroup
			};
		}
	}
</script>

<style lang="scss" scoped>
	.student-page {
		position: relative;
		border: #dddddd solid 1px;
		display: flex;
		flex-direction: column;

		.student-tip {
			position: absolute;
			top: 400rpx;
			padding: 50rpx;
			text-align: center;
			line-height: 50rpx;
			text-align: left;
			color: darkblue;
			font-size: 30rpx;
		}

		.nav-bar {
			padding: 2rpx 10rpx;
			display: flex;
			align-items: center;
			background-color: white;

			.nav-search {
				flex: 1;
			}

			.nav-add {
				cursor: pointer;
			}
		}

		.student-items {
			flex: 1;
			padding: 0;
			border: #dddddd solid 1px;
			overflow: hidden;
			position: relative;

			.scroll-bar {
				height: 100%;
			}
		}
	}
</style>